use crate::{Result};
use crate::parser::{TokenStream, Parser};
pub use crate::field::Field;
pub use crate::group::Group;
pub use crate::spec::{Spec, DecimalFormat, DecimalSpec, StringSpec, Sign};

pub trait RecordLike {
    fn level(&self) -> u32;
    fn name(&self) -> &str;
    fn length(&self) -> u32;
    fn offset(&self) -> u32;
}

#[derive(Debug)]
pub enum Record {
    Group(Group),
    Field(Field),
}

impl RecordLike for Record {
     fn level(&self) -> u32 {
        match self {
            Record::Group(g) => g.level(),
            Record::Field(f) => f.level(),
        }
    }
    fn length(&self) -> u32 {
        match self {
            Record::Group(g) => g.length(),
            Record::Field(f) => f.length(),
        }
    }
    fn name(&self) -> &str {
        match &self {
            Record::Group(g) => g.name(),
            Record::Field(f) => f.name(),
        }
    }
    fn offset(&self) -> u32 {
        match &self {
            Record::Group(g) => g.offset(),
            Record::Field(f) => f.offset(),
        }
    }
}

impl Record {
    pub fn collect_fields_into(&self, acc: &mut Vec<Field>) {
        match self {
            Record::Field(f) => acc.push(f.clone()),
            Record::Group(g) => {
                for entry in g.get_records() {
                    entry.collect_fields_into(acc);
                }
            }
        }
    }
}

impl Parser for Record {
    fn parse(stream: &mut TokenStream, offset: u32) -> Result<Self> {
        if let Ok(group) = Group::try_parse(stream, offset){
            Ok(Record::Group(group))
        } else if let Ok(field) = Field::try_parse(stream, offset) {
            Ok(Record::Field(field))
        } else {
            Err(stream.parse_error("entry"))
        }
    }
}

pub fn normalize_name(name: &str, stream: &TokenStream) -> String {
    let mut result = String::new();
    if name == "FILLER" {
        result.push_str(&format!("FILLER-LINE-{}", stream.row()));
    } else {
        result.push_str(name);
    }
    result
}
